package com.jiayou.config.vertx.handler

import cn.hutool.core.bean.BeanUtil
import cn.hutool.core.date.DateUtil
import cn.hutool.core.map.MapUtil
import cn.hutool.json.JSONUtil
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.serializer.SerializerFeature
import com.jiayou.config.vertx.VertxApplication
import com.jiayou.config.vertx.web.*
import io.vertx.core.Handler
import io.vertx.core.json.impl.JsonUtil
import io.vertx.ext.web.RoutingContext
import org.slf4j.LoggerFactory
import org.springframework.beans.BeanUtils
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.*
import kotlin.collections.ArrayList

/**
@author lishuang
@data   2021-07-19  10:46:28
@info   抽象的Handler，继承该handler可使用注解形式： GET 、 POST 、 PUT 、 DELETE
使用注解定义锚点参数列表只能有且仅有一个：RoutingContext
 */

//基于注解的handler
abstract class AnnotationBaseHandler : VertBaseHandler {
    companion object {
        public val httpMethods = arrayListOf(Get::class.java, Post::class.java, Put::class.java, Delete::class.java)

        @JvmStatic
        private val logger = LoggerFactory.getLogger(AnnotationBaseHandler::class.java)

        @JvmStatic
        val deployInfo = ArrayList<String>()

    }

    lateinit var vertxApplication: VertxApplication

    override fun init(vertxApplication: VertxApplication) {
        logger.info(this.javaClass.name + " handler has been load")
    }

    final override fun handle(vertxApplication: VertxApplication) {
        this.vertxApplication = vertxApplication
        this.analysisEndpoint()
    }

    //解析端点
    private fun analysisEndpoint() {
        val requestMapping = this.javaClass.getAnnotation(RequestMapping::class.java)
        if (requestMapping != null) {
            this.javaClass.methods.forEach { method ->
                method.annotations.forEach { annotation ->
                    when (annotation) {
                        is Get ->
                            if (annotation.block) {
                                vertxApplication.router.get(requestMapping.basePath + "/" + annotation.path)
                                        .blockingHandler(this.generateTarget(this, method, annotation.responseType))
                            } else {
                                vertxApplication.router.get(requestMapping.basePath + "/" + annotation.path)
                                        .handler(this.generateTarget(this, method, annotation.responseType))
                            }
                        is Post ->
                            if (annotation.block) {
                                vertxApplication.router.post(requestMapping.basePath + "/" + annotation.path)
                                        .blockingHandler(this.generateTarget(this, method, annotation.responseType))
                            } else {
                                vertxApplication.router.post(requestMapping.basePath + "/" + annotation.path)
                                        .handler(this.generateTarget(this, method, annotation.responseType))
                            }
                        is Put ->
                            if (annotation.block) {
                                vertxApplication.router.put(requestMapping.basePath + "/" + annotation.path)
                                        .blockingHandler(this.generateTarget(this, method, annotation.responseType))
                            } else {
                                vertxApplication.router.put(requestMapping.basePath + "/" + annotation.path)
                                        .handler(this.generateTarget(this, method, annotation.responseType))
                            }
                        is Delete ->
                            if (annotation.block) {
                                vertxApplication.router.delete(requestMapping.basePath + "/" + annotation.path)
                                        .blockingHandler(this.generateTarget(this, method, annotation.responseType))
                            } else {
                                vertxApplication.router.delete(requestMapping.basePath + "/" + annotation.path)
                                        .handler(this.generateTarget(this, method, annotation.responseType))
                            }
                    }
                }
            }
        } else {
            deployInfo.add(this.javaClass.name + "has bean injected in ioc but not deploy")
        }
    }

    //委托给目标对象执行方法
    private fun generateTarget(target: AnnotationBaseHandler, method: Method, responseType: ResponseType):
            Handler<RoutingContext> {
        return Handler<RoutingContext> { routingContext ->
            routingContext.response().putHeader("Content-Type", responseType.value)
//            val result: Any? = if (method.parameterTypes[0] != RoutingContext::class.java) {
//                method.invoke(target, *analysisMethodParams(routingContext, method))
//            } else {
//                val params = analysisMethodParams(routingContext, method).toMutableList()
//                params.add(0, routingContext)
//                method.invoke(target, *params.toTypedArray())
//            }
            val result: Any? = method.invoke(target, routingContext)
            if (result != null) {
                routingContext.response().end(JSON.toJSONString(result, SerializerFeature.WriteNullStringAsEmpty))
            } else {
                routingContext.response().end()
            }
        }
    }

    /**
     * 待开发.... 我去好难、想抄spring的参数绑定..
     */
    private fun analysisMethodParams(routingContext: RoutingContext, method: Method): Array<Any?> {
        val parameterTypes = method.parameters
        val inParams: Array<Any?>
        if (parameterTypes[0].type != RoutingContext::class.java) {
            inParams = Array(parameterTypes.size) {}
            for (paramsItem in parameterTypes.indices) {
                var currentObj: Any? = null
                try {
                    currentObj = JSONUtil.parse(routingContext.pathParams()).toBean(parameterTypes[paramsItem].type)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                inParams[paramsItem] = currentObj
            }
        } else {
            inParams = Array(parameterTypes.size - 1) {}
            for (paramsItem in parameterTypes.indices) {
                val currentParams = parameterTypes[paramsItem]
                if (paramsItem == 0) continue
                var currentObj: Any? = null
                try {
                    currentObj = when (currentParams.type) {
                        Char::class.java -> routingContext.request().params()[currentParams.name]
                        String::class.java -> routingContext.request().params()[currentParams.name].toString()
                        Int::class.java -> routingContext.request().params()[currentParams.name].toInt()
                        Double::class.java -> routingContext.request().params()[currentParams.name].toDouble()
                        Float::class.java -> routingContext.request().params()[currentParams.name].toFloat()
                        Long::class.java -> routingContext.request().params()[currentParams.name].toLong()
                        Short::class.java -> routingContext.request().params()[currentParams.name].toShort()
                        Byte::class.java -> routingContext.request().params()[currentParams.name].toByte()
                        Date::class.java -> DateUtil.parse(routingContext.request().params()[currentParams.name]).toJdkDate()
                        else -> JSONUtil.parse(routingContext.request().params()).toBean(parameterTypes[paramsItem].type)
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }

                inParams[paramsItem - 1] = currentObj
            }

        }
        return inParams
    }

}